import {
  Controller,
  Get,
  Header,
  Headers,
  HttpException,
  HttpStatus,
  Res,
  StreamableFile,
  UseInterceptors,
} from '@nestjs/common';
import { DownloadService, Megabyte } from './download.service';
import { Response } from 'express';
import { LoggerInterceptor } from 'src/interceptor/logger/logger.interceptor';
import { stat } from 'fs/promises';
import { join } from 'node:path';
import { createReadStream } from 'fs';

@Controller('download')
@UseInterceptors(LoggerInterceptor)
export class DownloadController {
  constructor(private readonly downloadService: DownloadService) {}

  @Get('/simple')
  @Header('Content-Disposition', `attachment; filename=1.txt`)
  downloadSimpleFile() {
    return this.downloadService.downloadSimpleFile();
  }

  @Get('/simple/cus/header')
  @Header('Content-Disposition', `attachment; filename=1.txt`)
  async downloadSimpleFileWithCusHeader(@Res() response: Response) {
    // 自定义头部
    response.attachment();
    response.contentType('map');
    response.send(await this.downloadService.downloadSimpleFile());
  }

  @Get('/buffer')
  @Header('Content-Disposition', `attachment; filename=buffer.txt`)
  downloadSimpleFileWithBuffer(@Res() response: Response) {
    response.send(this.downloadService.downloadSimpleFileWithBuffer());
  }

  @Get('/stream')
  @Header('Content-Disposition', `attachment; filename=stream.map`)
  async downloadSimpleFileWithStream(@Res() response: Response) {
    this.downloadService.downloadSimpleFileWithStream().pipe(response);
  }

  @Get('/streamable')
  async downloadSimpleFileWithStreamable(
    @Res({ passthrough: true }) response: Response,
  ) {
    response.attachment('streamable.map');
    return new StreamableFile(
      this.downloadService.downloadSimpleFileWithStreamable(),
    );
  }

  @Get('/big/pipe/streamable')
  async downloadSimpleFileWithStreamableBig(@Res() response: Response) {
    response.setHeader('connection', 'keep-alive');
    response.attachment('streamable.big.map');
    this.downloadService.downloadSimpleFileWithStreamableBig().pipe(response);
  }

  @Get('/big/class/streamable')
  async downloadSimpleFileWithStreamableBigWithClass(
    @Res({
      passthrough: true,
    })
    response: Response,
  ) {
    response.setHeader('connection', 'keep-alive');
    response.attachment('streamable.big.map');
    return new StreamableFile( // 需要配合 passthrough 一起使用
      this.downloadService.downloadSimpleFileWithStreamableBig(),
    );
  }

  @Get('/big/range/streamable')
  @Header('Connection', 'keep-alive')
  async downloadSimpleFileWithStreamableBigWithRange(
    @Res({
      passthrough: true,
    })
    response: Response,
    @Headers('range') range: string,
  ) {
    response.attachment(`pnpm-lock.yaml`);
    const CHUNK_SIZE = 10 * Megabyte;
    const p = join(process.cwd(), `pnpm-lock.yaml`);
    const fileStat = await stat(p);
    if (fileStat.isFile() && range) {
      const start = Number(range.replace(/\D/g, ''));
      const end = Math.min(start + CHUNK_SIZE, fileStat.size - 1);
      response.setHeader(
        'Content-Range',
        `bytes ${start}-${end}/${fileStat.size}`,
      );
      response.setHeader('Accept-Ranges', 'bytes');
      response.setHeader('Content-Length', end - start + 1);

      return new StreamableFile( // 需要配合 passthrough 一起使用
        createReadStream(p, {
          start,
          end,
        }),
      );
    } else {
      throw new HttpException(
        '范围无效',
        HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE,
      );
    }
  }
}
